package lion.framework.core.web.path;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lion.dev.lang.Proxy;
import lion.framework.core.bean.BeanFactory;
import lion.framework.core.db.SessionFactory;
import lion.framework.core.web.anno.Path;
import lion.framework.core.web.exception.WebException;

/**
 * 异步请求执行类，异步请求将不对事务进行控制,异步请求的结束通过 {@link HttpServletResponse}
 * .getWriter().close()方法或等待浏览器自动超时断开。 <br/>
 * tips: <br/>
 * 1、如何区分异步与同步请求，在 {@link Path} 中有一个属性设置为async=true,默认该属性是false。<br/>
 * 2、 如何断定该请求就是异步的？<br/>
 * 在异步请求中，请求进入的线程与请求方法执行的线程不是同一个线程。如果需要测试，可以自行增加一个拦截器，然后打印Thread.currentThread()
 * .getName()观察,如果同步请求，打印出来的将会是一样的，异步则不同。
 *
 * <pre>
 * time   2013年9月29日
 * mail   hl_0074@sina.com
 * </pre>
 *
 * @author lion
 */
public class AsyncMethodExecutor implements Runnable {

	private Object[] params;
	private PathNode node;
	private AsyncContext ctx;

	public AsyncMethodExecutor(AsyncContext ctx, PathNode node, Object[] params) {

		this.ctx = ctx;
		this.params = params;
		this.node = node;

	}

	@Override
	public void run() {

		try {
			execute();
		} catch (Exception e) {
			PathNodeExecutor.onException((HttpServletRequest) ctx.getRequest(), (HttpServletResponse) ctx.getResponse(), e);
		} finally {
			SessionFactory.cleanSession();
			ctx.complete();
		}
	}

	private void execute() {

		Method method = node.getMethod();
		Object target = null;
		if (Modifier.isStatic(method.getModifiers())) {
			target = method.getDeclaringClass();
		} else {
			if (node.isSingleton()) {
				try {
					target = BeanFactory.getBeanFactory(BeanFactory.PLATFORM_BEAN_FACTORY).get("lion.framework$" + method.getDeclaringClass().getName(),
							method.getDeclaringClass());
				} catch (Throwable e) {
					throw new WebException(500, "Internal Server Error", e);
				}
			} else {
				try {
					target = BeanFactory.create(method.getDeclaringClass());
				} catch (Throwable e) {
					throw new WebException(500, "Internal Server Error", e);
				}
			}
		}

		target = Proxy.proxy(target, new ExecuteInterceptor());
		try {
			method.setAccessible(true);
			method.invoke(target, params);
		} catch (Throwable e) {
			throw new WebException(500, "Internal Server Error", e);
		}
	}
}
